﻿using System;
using System.Text;

namespace FuzzScript
{
    /// <summary>
    /// Fuzzy Control System root node. A system has three siblings:
    /// 1. A Knowledge Base
    /// 2. A list of Fuzzy predicates that are used in the rule base
    /// 3. The rule base that determines the output values
    /// </summary>
    class FuzzySystem : Node
    {
        /// <summary>
        /// Constructor. Initialized to three siblings
        /// </summary>
        internal FuzzySystem()
        {
            m_SiblingNodes.Add(null);
            m_SiblingNodes.Add(null);
            m_SiblingNodes.Add(null);
        }

        internal INode Predicates
        {
            set { m_SiblingNodes[1] = value; }
            get { return (PredicatesNode)m_SiblingNodes[1]; }
        }
        internal INode KnowledgeBase
        {
            set { m_SiblingNodes[0] = value; }
            get { return m_SiblingNodes[0]; }
        }

        internal INode RuleBase
        {
            set { m_SiblingNodes[2] = value; }
            get { return m_SiblingNodes[2]; }
        }


        public override double Execute()
        {
            Predicates.Execute();
            RuleBase.Execute();
            return 0;
        }

        public override void AddSibling(INode p_SiblingNode)
        {
            throw new NotImplementedException();
        }

        public override string Generate()
        {
            StringBuilder sCode = new StringBuilder();
            sCode.Append("using System.Collections.Generic;\nusing System;\nclass FuzzyOptimizedEngine : FuzzScript.IControlEngine{\n\n");

            sCode.Append(KnowledgeBase.Generate());

            // append all predicate information
            sCode.Append(Predicates.Generate());

            // add all general methods
            sCode.Append(GetGeneratedSystemMethods());

            sCode.Append("\nprivate void ExecuteRuleBase()\n{\n");
            sCode.Append("double dAlpha;\n");
            sCode.Append(RuleBase.Generate());
            sCode.Append("\n}\n");
            sCode.Append("}");

            return sCode.ToString();
        }

        private string GetGeneratedSystemMethods()
        {
            return @"        
            private int VariableToResolution(double p_dVal, double p_dUpperVal, double p_dLowerVal)
            {
                if (p_dVal > p_dUpperVal) 
                    p_dVal = p_dUpperVal;

                if (p_dVal < p_dLowerVal)
                    p_dVal = p_dLowerVal;

                int iRet = (int)(((p_dVal - p_dLowerVal) / (p_dUpperVal - p_dLowerVal)) * (" + FuzzyVariable.Resolution + @" ));

                return iRet;
            }

            private double ResolutionToVariable(int p_iVal, double p_dUpperVal, double p_dLowerVal)
            {
                if (p_iVal < 0 && p_iVal > (" + FuzzyVariable.Resolution + @"))
                {
                    p_iVal = 0;
                }

                return (((double)p_iVal / (double)(" + FuzzyVariable.Resolution + @")) * (p_dUpperVal - p_dLowerVal)) + p_dLowerVal;
            }
    

            internal void MaxMin(double[] p_OutputSet,  double[] p_ClauseSet,  double p_dAlpha)
            {
                for (int i = 0; i < " + FuzzyVariable.Resolution + @"; i++)
                {
                    p_OutputSet[i] = Math.Max(p_OutputSet[i], Math.Min(p_ClauseSet[i], p_dAlpha));
                }
            }

            internal void SetDiscreteDoM(double[] p_OutputSet, int p_iVariableVal, double p_dDoM)
            {
                p_OutputSet[p_iVariableVal] = p_dDoM;
            }

            internal int CoGDefuzzification(double[] p_Set)
            {

                    double fNumerator = 0;
                    double fDenominator = 0;
                    double fCrispVal = 0;

                    for (int i = 0; i < " + FuzzyVariable.Resolution + @"; i++)
                    {
                        fNumerator += (i * p_Set[i]);
                        fDenominator += p_Set[i];
                    }

                    if (fDenominator > 0)
                        fCrispVal = fNumerator / fDenominator;
                    else
                        fCrispVal = " + FuzzyVariable.Resolution + @" / 2;

                    return (int)Math.Round(fCrispVal);
            }

            ";
        }

        public override string ToString()
        {
            StringBuilder builder = new StringBuilder();

            builder.Append("\n\nKnowledge Base\n");

            builder.Append(m_SiblingNodes[0].ToString());

            builder.Append("\n\nPredicates\n");

            builder.Append(m_SiblingNodes[1].ToString());

            builder.Append("\n\nRule Base\n");

            builder.Append(m_SiblingNodes[2].ToString());

            return builder.ToString();
        }
    }
}
